<?php

	namespace Phptricks\system\library\models;

/*
	 *  phptricks Framework 
	 *  link http://phptricks.net/Framework
	 *  date start 2014
	 */
	/**
	 * Description of newPHPClass
	 *
	 * @author Abd Elfttah Ahmed <phptricks.net@gmail.com>
	 */
	use Phptricks\system\core\library\library;
	use Phptricks\system\library\PhptricksExceptions\PhptricksExceptions as Exc;
	use Phptricks\system\library\database\database as DataBase;
	use Phptricks\system\library\database\database_helper\database_helper as database_helper;
	use Phptricks\system\library\custom_fields\custom_fields as custom_fields;

	class models implements library{

		private static $_instance = null;
		private $config;
		public $db;
		public $fatch_mode = null;
		public $_error = array();
		public $_max_row;
		public $_count_row;
		public $helper = null;
		private $_query_string;
		private $_where;
		public $custom_fields = null;

		public function __construct(){
			$this->db = DataBase::getinstance();
			$this->helper = database_helper::getinstance();
			$this->custom_fields = custom_fields::getinstance();
			$this->config = $this->db->_config;
			$this->_count_row = 0;
			$this->_max_row = $this->config->query_limit;
			$this->fatch_mode = $this->db->fatch_mode;
		}

		public static function getinstance(){
			if (self::$_instance === null) {
				self::$_instance = new \Phptricks\system\library\models\models();
			}
			return self::$_instance;
		}

		public function _initialize(){
			
		}

		/**
		 * @param string $Query_String
		 * @param array $where
		 * @param string $fatch_type 
		 */
		public function Query($Query_String, $where = '', $type = 'AND', $sort_by = '', $fatch_type = 'FETCH_ASSOC', $_limit = ''){
			if (empty($type)) {
				$type = 'AND';
			}
			try {
				$fatch_type = \strtoupper($fatch_type);
				if (isset($fatch_type)) {
					if (isset($this->fatch_mode[$fatch_type])) {
						$fatch_type = $this->fatch_mode[$fatch_type];
					} else {
						$fatch_type = $this->fatch_mode['FETCH_ASSOC'];
					}
				}

				if (isset($where) && !empty($where)) {
					$Query_String = $Query_String . " " . $this->get_where($where, $type);
				}
				if (!empty($sort_by)) {
					$sort_by = ' ORDER BY ' . $sort_by;
				}
				if (!empty($_limit)) {
					$_limit = ' LIMIT ' . $_limit;
				} else {
					$_limit = ' LIMIT ' . $this->db->_query_limit;
				}
				$Query = $this->db->prepare($Query_String . $sort_by . $_limit);
				$Query->execute();
				$this->_count_row = $Query->rowCount();
				$error_info = $Query->errorInfo();
				if (!empty($error_info[1]) > 0) {
					$error_message = $error_info[2] . "<br>" . $Query_String;
					throw new Exc($error_message);
				}
				return $Query->fetchAll($fatch_type);
			} catch (\PDOException $exc) {
				throw new Exc($exc->getTraceAsString());
			}
		}

		/**
		 * 
		 * @param type $table_name
		 * @param type $array_data
		 * @return boolean
		 * @throws Exc
		 */
		public function insert($table_name, $array_data = array()){
			if (!isset($table_name) || empty($table_name)) {
				throw new Exc('The $table field must have a value');
			}
			if (!isset($array_data) || empty($array_data) || \count($array_data) <= 0) {
				throw new Exc('The $array_data field must have a value');
			}
			if (!\is_array($array_data)) {
				throw new Exc('The value $array_data must be Array');
			}
			try {
				$feild_keys = "`" . \implode("`, `", array_keys($array_data)) . "`";
				$feild_values = "'" . \implode("', '", \array_values($array_data)) . "'";
				$insert = $this->db->prepare("INSERT INTO " . $table_name . " ( " . $feild_keys . " ) VALUES ( " . $feild_values . " )");
				$insert->execute();
				$error_info = $insert->errorInfo();
				if (!empty($error_info[1]) > 0) {
					throw new Exc($error_info[2]);
				}
				$this->_insert_id = $this->db->lastInsertId();
			} catch (\PDOException $e) {
				throw new Exc($e->getTraceAsString());
			}
			return true;
		}

		/**
		 * 
		 * @param type $table
		 * @param type $fields
		 * @param type $where
		 * @return boolean
		 * @throws Exc
		 */
		public function update($table, $fields = array(), $where = array()){
			try {
				if (empty($table)) {
					throw new Exc("Error : Table Not Set ");
				}
				if (empty($fields)) {
					throw new Exc("Error : Field Is Not Set");
				}
				if (isset($where) && \count($where) <= 0) {
					throw new Exc("Error : Field Is Empty");
				}
				$array_update = array();
				foreach ($fields as $key => $val) {
					$array_update[] = $key . "='" . $val . "'";
				}

				$where_text = '';
				if (isset($where) || !empty($where)) {
					$where_text = $this->get_where($where);
				}
				$table = $this->db->_prefix . $table;
				$values = \implode(", ", $array_update);
				$updatesql = 'UPDATE ' . $table . ' SET ' . $values;
				if (!empty($where_text)) {
					$updatesql = 'UPDATE ' . $table . ' SET ' . $values . ' ' . $where_text;
				}
				$update = $this->db->prepare($updatesql);
				$error_info = $update->errorInfo();
				if (!empty($error_info[1]) > 0) {
					throw new Exc($error_info[2] . "<br>" . $updatesql);
				}
				$update->execute();
			} catch (\PDOException $e) {
				throw new Exc($e->getTraceAsString());
			}
			return true;
		}

		/**
		 * 
		 * @param type $table
		 * @param type $where
		 * @return boolean
		 * @throws Exc
		 */
		public function delete($table, $where = array()){
			try {
				if (empty($table)) {
					throw new Exc("Error : Table Not Set ");
				}
				$where_text = '';
				if (isset($where) || !empty($where)) {
					$where_text = $this->get_where($where);
				}
				$table = $this->db->_prefix . $table;
				$delete = $this->db->prepare("DELETE FROM " . $table . $where_text);
				$delete->execute();
			} catch (\PDOException $e) {
				throw new Exc($e->getTraceAsString());
			}
			if (empty($this->_error)) {
				return true;
			} else {
				$this->_error = \implode("<br>", $this->_error);
				throw new Exc($this->_error);
			}
		}

		/**
		 * 
		 * @param string $fields
		 * @param type $tables
		 * @return $this
		 */
		public function select($fields, $tables){
			if (empty($tables) && !isset($tables)) {
				throw new Exc('$tables is not set');
			}
			if (empty($fields) && !isset($fields)) {
				throw new Exc('$fields is not set');
			} else if (empty($fields)) {
				$fields = '*';
			}
			if (\strpos($tables, ',')) {
				$tables = \explode(',', $tables);
				foreach ($tables as $key => $value) {
					$tables[$key] = $this->db->_prefix . \preg_replace('/\s+/i', '', $value);
				}
				$tables = implode(', ', $tables);
			} else {
				$tables = $this->db->_prefix . $tables;
			}
			$this->_query_string = \sprintf("SELECT %s FROM %s", $fields, $tables);
			return $this;
		}

		/**
		 * 
		 * @param type $where_data
		 * @param type $type
		 */
		public function where($where_data, $type = 'AND'){
			$this->_where = $this->get_where($where_data, $type);
			return $this;
		}

		/**
		 * 
		 * @param type $fatch_type
		 * @param string $order_by
		 * @param string $group_by
		 * @param type $_limit
		 */
		public function execute($fatch_type = '', $order_by = '', $group_by = '', $_limit = ''){
			$fatch_type = \strtoupper($fatch_type);
			if (isset($fatch_type)) {
				if (isset($this->fatch_mode[$fatch_type])) {
					$fatch_type = $this->fatch_mode[$fatch_type];
				} else {
					$fatch_type = $this->fatch_mode['FETCH_ASSOC'];
				}
			} else {
				$fatch_type = $this->fatch_mode['FETCH_ASSOC'];
			}
			if (!empty($order_by)) {
				$order_by = ' ORDER BY ' . $order_by;
			}
			if (!empty($group_by)) {
				$group_by = ' GROUP BY ' . $group_by;
			}
			if ($_limit !== null) {
				if (empty($_limit)) {
					$_limit = ' LIMIT ' . $this->db->_query_limit;
				} else {
					$_limit = ' LIMIT ' . $_limit;
				}
			} else {
				$_limit = '';
			}

			$this->_query_string = $this->_query_string . $this->_where . $group_by . $order_by . $_limit;
			try {
				$query = $this->db->prepare($this->_query_string);
				$query->execute();
				$this->_count_row = $query->rowCount();
				$error_info = $query->errorInfo();
				if (!empty($error_info[1]) > 0) {
					$error_message = $error_info[2] . "<br>" . $this->_query_string;
					throw new Exc($error_message);
				}
				$this->db->_list_query[] = $this->_query_string;
				$this->_query_string = '';
				$this->_where = '';
				return $query->fetchAll($fatch_type);
			} catch (\PDOException $e) {
				throw new Exc($e->getTraceAsString());
			}
		}

		/**
		 * 
		 * @param type $where
		 * @param type $type
		 * @return string
		 * @throws Exc
		 */
		public function get_where($where, $type){
			try {
				if (!empty($type)) {
					if (\strtolower($type) == 'and') {
						$type = ' AND ';
					} else {
						$type = ' OR ';
					}
				} else {
					$type = ' AND ';
				}
				$where_text = '';
				if (isset($where) && !empty($where) && \is_array($where)) {
					$where_array = array();
					foreach ($where as $keys => $vals) {
						if (\strpos($keys, '|')) {
							$keys = \explode('|', $keys);
							switch (\strtolower($keys[1])) {
								case 'between':
									$where_array[] = $keys[0] . " BETWEEN " . $vals[0] . " AND " . $vals[1];
									break;
								case 'like':
									$where_array[] = $keys[0] . " LIKE '%" . $vals . "%'";
									break;
								case 'number':
									$where_array[] = $keys[0] . " = " . $vals;
									break;
								default :
									$where_array[] = $keys . " = '" . $vals . "'";
									break;
							}
						} else {
							if (\is_array($vals)) {
								$where_array[] = $keys . " IN ('" . \implode("','", $vals) . "')";
							} else {
								$where_array[] = $keys . " = '" . $vals . "'";
							}
						}
					}
					$where_text = ' WHERE ' . \implode($type, $where_array);
				}
			} catch (\Exception $exc) {
				throw new Exc($exc->getTraceAsString());
			}
			return $where_text;
		}

		/**
		 * 
		 * @param string $table
		 * @param string|array $where 
		 * @param string $type
		 * @param string $fatch_type
		 * @return integer count of query
		 */
		public function _CountRow($table, $where = '', $group_by = '', $type = 'AND', $fatch_type = 'FETCH_ASSOC'){
			if (isset($where) && !empty($where)) {
				$where = $this->get_where($where, $type);
			}
			if (!empty($group_by)) {
				$group_by = ' GROUP BY ' . $group_by . ' ';
			}
			$table = $this->db->_prefix . $table;
			$this->_query_string = "SELECT COUNT(*) as row_count FROM " . $table . " " . $where . " " . $group_by;
			$Query = $this->execute('', '', '', 100000000);
			$row_data = $Query;
			$this->_count_row = $row_data[0]['row_count'];
			return $this->_count_row;
		}

	}
	